﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BricksProblemSolver.Solution1
{
    public class BrickRow1 : IBrickRow
    {
        private static Dictionary<int, string> s_brickStringDic = new Dictionary<int, string>();

        private sbyte[] _bricks;
        private bool _seal;
        private int _count;
        private int _length;
        private int _hashCode;
        private int _currentBricksLength;

        public BrickRow1(int maxBricksCount, int length)
        {
            _bricks = new sbyte[maxBricksCount];
            _length = length;
        }

        public int Count
        {
            get
            {
                return _count;
            }
        }

        public int MaxCount
        {
            get
            {
                return _bricks.Length;
            }
        }

        public int Length
        {
            get
            {
                return _length;
            }
        }

        public bool IsFull
        {
            get
            {
                return _currentBricksLength == Length;
            }
        }

        public void Clear()
        {
            _seal = false;
            _bricks = new sbyte[_bricks.Length];
            _currentBricksLength = 0;
            _count = 0;
        }

        public void Seal()
        {
            _seal = true;
            _hashCode = CalculateHashCode();
        }

        public override string ToString()
        {
            return string.Join("|", _bricks.Where(x => x > 0).Select(x => GetBrickString(x)));
        }

        public override bool Equals(object obj)
        {
            if (ReferenceEquals(obj, this))
            {
                return true;
            }
            var other = obj as BrickRow1;
            if (other == null)
            {
                return true;
            }
            for(int i = 0; i < MaxCount; ++i)
            {
                if (_bricks[i] != other._bricks[i])
                {
                    return false;
                }
            }
            return true;
        }

        public override int GetHashCode()
        {
            if (_seal)
            {
                return _hashCode;
            }
            return CalculateHashCode();
        }

        public object Clone()
        {
            var clone = new BrickRow1(_bricks.Length, Length);
            clone._currentBricksLength = _currentBricksLength;
            clone._count = Count;
            Array.Copy(_bricks, clone._bricks, _bricks.Length);
            return clone;
        }

        private static string GetBrickString(int length)
        {
            if (length <= 0)
            {
                return string.Empty;
            }
            if (!s_brickStringDic.ContainsKey(length))
            {
                lock (s_brickStringDic)
                {
                    if (!s_brickStringDic.ContainsKey(length))
                        s_brickStringDic[length] = new string(Enumerable.Range(0, length - 1).Select(i => '_').ToArray());
                }
            }
            return s_brickStringDic[length];
        }

        private int CalculateHashCode()
        {
            if (_bricks == null)
            {
                return 0;
            }
            unchecked
            {
                return (int)long.Parse(string.Join("", _bricks.Take(Count)));
            }
        }

        public void AddBrick(int brickLength)
        {
            lock(this)
            {
                if (CanAddBrick(brickLength))
                {
                    _currentBricksLength += brickLength;
                    _bricks[_count++] = (sbyte)brickLength;
                    if (IsFull)
                    {
                        Seal();
                    }
                }
            }
        }

        public bool CanAddBrick(int brickLength)
        {
            return Length - _currentBricksLength >= brickLength;
        }

        public bool HasNoGapOnSameLine(IBrickRow otherRow)
        {
            var compareRow = (BrickRow1)otherRow;
            int sumOfThis = _bricks[0], sumOfAnother = compareRow._bricks[0];

            for (int i = 1, j = 1; (i < Count - 1) || (j < compareRow.Count - 1);)
            {
                if (sumOfThis > sumOfAnother)
                {
                    sumOfAnother += compareRow._bricks[j++];
                }
                else if (sumOfThis < sumOfAnother)
                {
                    sumOfThis += _bricks[i++];
                }
                else
                {
                    return false;
                }
            }
            return sumOfAnother != sumOfThis;
        }

        public IEnumerable<sbyte> GetAllBricks()
        {
            return new ArraySegment<sbyte>(_bricks, 0, Count);
        }
    }
}
